package microcredit.gui.util;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.Locale;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

import microcredit.Version;
import microcredit.gui.base.BaseDialog;
import microcredit.model.ValidationException;
import net.miginfocom.swing.MigLayout;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jfree.ui.UIUtilities;

import com.jidesoft.swing.MultilineLabel;

public class ExceptionDialog extends BaseDialog {

	private static final Log LOGGER = LogFactory.getLog(ExceptionDialog.class);
	
	private static final long serialVersionUID = 1L;
	private JLabel titleLabel;
	private JScrollPane scrollPane;
	private JTextArea exceptionTextArea;
	private MultilineLabel exceptionLabel;
	private JButton btnCopy;
	private JButton btnClose;
	private JLabel lblPleaseCopyAnd;
	private JLabel title2Label;

	public ExceptionDialog(Component parent, Throwable e) {
		super(DialogUtilities.getWindowForComponent(parent),
				ModalityType.TOOLKIT_MODAL);

		getContentPane().setBackground(new Color(255, 235, 205));
		setTitle("Oops! Sometimes things do not work as expected ;-)");
		getRootPane().setDefaultButton(getBtnCopy());
		getContentPane().setLayout(
				new MigLayout("", "[grow][]", "[][][][grow][]"));
		getContentPane().add(getTitleLabel(), "cell 0 0 2 1");
		getContentPane().add(getTitle2Label(), "cell 0 1");
		getContentPane().add(getExceptionLabel(), "cell 0 2 2 1,grow");
		getContentPane().add(getScrollPane(), "cell 0 3 2 1,grow");
		getContentPane().add(getLblPleaseCopyAnd(),
				"flowx,cell 0 4,alignx right");
		getContentPane().add(getBtnCopy(), "cell 0 4,alignx right");
		getContentPane().add(getBtnClose(), "cell 1 4");

		getExceptionLabel().setText(e.toString());
		getExceptionTextArea().setText(formatException(e));

		setSize(668, 500);
		setResizable(false);
		setLocationRelativeTo(parent);
		setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		
		getRootPane().getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
		getRootPane().getActionMap().put("close", new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent e) {
				dispose();
			}
		});
		
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowOpened(WindowEvent e) {
				getExceptionTextArea().moveCaretPosition(0);
			}
		});
	}

	private JLabel getTitleLabel() {
		if (titleLabel == null) {
			titleLabel = new JLabel("Ooops! An error occured: ");
			titleLabel.setFont(titleLabel.getFont().deriveFont(
					titleLabel.getFont().getStyle() | Font.BOLD,
					titleLabel.getFont().getSize() + 4f));
		}
		return titleLabel;
	}

	private JScrollPane getScrollPane() {
		if (scrollPane == null) {
			scrollPane = new JScrollPane();
			scrollPane.setBackground(new Color(255, 235, 205));
			scrollPane.setViewportView(getExceptionTextArea());
		}
		return scrollPane;
	}

	private JTextArea getExceptionTextArea() {
		if (exceptionTextArea == null) {
			exceptionTextArea = new JTextArea();
			exceptionTextArea.setFont(new Font("Courier New", Font.PLAIN, 11));
			exceptionTextArea.setEditable(false);
		}
		return exceptionTextArea;
	}

	private MultilineLabel getExceptionLabel() {
		if (exceptionLabel == null) {
			exceptionLabel = new MultilineLabel();
			exceptionLabel.setFont(exceptionLabel.getFont().deriveFont(
					exceptionLabel.getFont().getSize()));
			exceptionLabel.setBackground(new Color(255, 235, 205));
		}
		return exceptionLabel;
	}

	private JButton getBtnCopy() {
		if (btnCopy == null) {
			btnCopy = new JButton("Copy");
			btnCopy.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					StringSelection data = new StringSelection(getExceptionTextArea().getText());
					Clipboard clipboard = Toolkit.getDefaultToolkit()
							.getSystemClipboard();
					clipboard.setContents(data, data);
					MessageUtilities.showInformation(ExceptionDialog.this, "Successful", "Error message was copied successful!");
				}
			});
			btnCopy.setBackground(new Color(233, 150, 122));
		}
		return btnCopy;
	}

	private JButton getBtnClose() {
		if (btnClose == null) {
			btnClose = new JButton("Close");
			btnClose.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					dispose();
				}
			});
		}
		return btnClose;
	}

	private JLabel getLblPleaseCopyAnd() {
		if (lblPleaseCopyAnd == null) {
			lblPleaseCopyAnd = new JLabel(
					"Please copy and report the error -->");
		}
		return lblPleaseCopyAnd;
	}

	public static void show(Component parent, Throwable t) {
		ExceptionDialog dialog = new ExceptionDialog(parent, t);
		dialog.setVisible(true);
	}

	public static void handleException(Component parent, Throwable t) {
		Window w = DialogUtilities.getWindowForComponent(parent);
		if (t instanceof ValidationException) {
			handleValidationError((ValidationException)t, w);
		} else {
			LOGGER.error(formatException(t));
			show(w, t);
		}
	}

	private static void handleValidationError(ValidationException ex, Window w) {
		String message = ex.getMessage();
		String title = "Validation error";
		
		UIUtil.clearValidations(w);
		
		String fieldName = ex.getFieldName();
		boolean success = false;
		if (fieldName != null) {
			success = UIUtil.applyNotification(w, fieldName, message);
		}
		if (!success) {
			JOptionPane.showMessageDialog(w, message, title, JOptionPane.ERROR_MESSAGE);
		}
	}
	
	private static String formatException(Throwable exception) {
		StringBuilder buffer = new StringBuilder();
		buffer.append(fill("", '#', 80, true));
		buffer.append("\n# UNEXPECTED INTERNAL ERROR ");
		buffer.append("\n# ");
		if (exception != null) {
			buffer.append(exception.getClass().getName());
			buffer.append(": ");
			if (exception.getMessage() != null) {
				buffer.append(exception.getMessage());
			} else {
				buffer.append("No message available");
			}
		} else {
			buffer.append("No exception available");
		}
		buffer.append("\n# Time occured: ");
		buffer.append(DateFormat.getDateTimeInstance(DateFormat.FULL,
				DateFormat.FULL, Locale.ENGLISH).format(
				new Timestamp(System.currentTimeMillis())));

		buffer.append("\n# Java version: ");
		buffer.append(System.getProperty("java.version"));
		buffer.append("\n# Operating system: ");
		buffer.append(System.getProperty("os.name"));
		buffer.append(" - ");
		buffer.append(System.getProperty("os.version"));
		if (System.getProperty("sun.os.patch.level") != null) {
			buffer.append(" ");
			buffer.append(System.getProperty("sun.os.patch.level"));
		}

		buffer.append("\n#");
		buffer.append("\n# Version: ");
		buffer.append(Version.VERSION);
		buffer.append(fill("\n", '#', 80, false) + "\n\n");

		CharArrayWriter writer = new CharArrayWriter();
		if (exception != null) {
			exception.printStackTrace(new PrintWriter(writer));
		}
		buffer.append(writer.toCharArray());

		return buffer.toString();
	}

	private static String fill(String value, char filler, int length,
			boolean beforeVal) {
		int srcLen = value.length();
		if (srcLen < length) {
			char[] arr = new char[length];
			int fillLength = length - srcLen;
			int start = beforeVal ? fillLength : 0;
			int fillStart = beforeVal ? 0 : srcLen;
			System.arraycopy(value.toCharArray(), 0, arr, start, srcLen);
			for (int i = 0; i < fillLength; i++) {
				arr[fillStart + i] = filler;
			}
			return String.copyValueOf(arr);
		}
		return value;
	}
	private JLabel getTitle2Label() {
		if (title2Label == null) {
			title2Label = new JLabel("Please report this error to the developers using the copy to clipboard button!");
		}
		return title2Label;
	}
}
